package com.egao.common.module.notice.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.egao.common.component.wechat.WeChatUtils;
import com.egao.common.core.Constants;
import com.egao.common.core.exception.BusinessException;
import com.egao.common.core.utils.CoreUtil;
import com.egao.common.core.utils.ExcelUtils;
import com.egao.common.core.web.JsonResult;
import com.egao.common.core.web.PageParam;
import com.egao.common.module.notice.entity.Notice;
import com.egao.common.module.notice.entity.NoticeRecord;
import com.egao.common.module.notice.mapper.NoticeMapper;
import com.egao.common.module.notice.service.NoticeRecordService;
import com.egao.common.module.notice.service.NoticeService;
import com.egao.common.module.notice.utils.NoticeParams;
import com.egao.common.module.notice.utils.NoticeRecordExport;
import com.egao.common.module.student.entity.Student;
import com.egao.common.module.student.service.StudentService;
import com.egao.common.module.system.constants.UserConstants;
import com.egao.common.module.system.entity.User;
import com.egao.common.module.system.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * notice service impl
 *
 * @author root
 */
@Service
public class NoticeServiceImpl extends ServiceImpl<NoticeMapper, Notice> implements NoticeService {

    @Autowired
    private UserService userService;
    @Autowired
    private StudentService studentService;
    @Autowired
    private NoticeRecordService noticeRecordService;

    /**
     * 推送成功
     */
    private final String SUCCESS = "0";
    /**
     * 推送失败
     */
    private final String FAIL = "1";

    @Override
    public List<Notice> listPage(PageParam<Notice> page) {
        String remark = page.getString("remark");
        String content = page.getString("content");
        PageParam<Notice> noticePageParam = baseMapper.selectPage(page,
                new QueryWrapper<Notice>()
                        .like(!StrUtil.isBlank(remark), "remark", remark)
                        .like(!StrUtil.isBlank(content), "content", content)
                        .orderByDesc("create_time")
        );
        return noticePageParam.getRecords();
    }

    @Override
    public JsonResult send(NoticeParams noticeParams, Integer loginUserId) {
        // 构造参数
        String content = noticeParams.getContent();
        String remark = noticeParams.getRemark();

        List<String> list = buildXhs(noticeParams);

        int length = list.size();
        if (length == 0) {
            return JsonResult.error("请选择推送接收人");
        }

        String comment = "";
        // 保存本次推送
        Notice notice = new Notice();
        notice.setContent(content);
        notice.setRemark(remark);
        notice.setNumber(length);
        notice.setCreateBy(loginUserId);
        baseMapper.insert(notice);

        List<NoticeRecord> recordAllList = new ArrayList<>();

        //分2000人推送一次
        int groupSize = 2000;
        //计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize;
        for (int i = 0; i < num; i++) {
            //开始位置
            int fromIndex = i * groupSize;
            //结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            // 2000一推
            List<String> newList = list.subList(fromIndex, toIndex);
            String[] sends = newList.toArray(new String[newList.size()]);
            JsonResult pushResult = WeChatUtils.push(sends, content);

            comment += JSONUtil.toJsonStr(pushResult.getData()) + "&";

            // 例子
            //String str = "{\"e\":0,\"m\":\"操作成功\",\"d\":{\"ucs_id\":13322049,\"error_data\":{\"1\":\"123*\",\"2\":\"456*\"}}}";
            //JsonResult pushResult = JsonResult.ok().setData(str);

            List<NoticeRecord> recordList = saveRecord(newList, pushResult, notice.getId());
            recordAllList.addAll(recordList);
        }

        // 保存成功失败个数
        long success = recordAllList.stream().filter(d -> d.getState().equals(SUCCESS)).count();
        long fail = recordAllList.stream().filter(d -> d.getState().equals(FAIL)).count();
        notice.setSuccess((int) success);
        notice.setFail((int) fail);
        notice.setComment(comment);
        baseMapper.updateById(notice);

        return JsonResult.ok("发送成功");
    }

    /**
     * 构建当前推送人集合
     *
     * @param noticeParams
     * @return
     */
    private List<String> buildXhs(NoticeParams noticeParams) {
        List<String> list = new ArrayList<>();

        String strings = "";

        if (!StrUtil.isBlank(noticeParams.getInputXhs())) {
            strings += noticeParams.getInputXhs();
        }

        if (!StrUtil.isBlank(noticeParams.getExcelXhs())) {
            strings += noticeParams.getExcelXhs();
        }

        List<String> userTypes = noticeParams.getUserTypes();
        List<String> yearIds = noticeParams.getYearIds();
        List<String> xls = noticeParams.getXls();
        List<String> collegeCodes = noticeParams.getCollegeCodes();

        if (null != userTypes && userTypes.size() > 0 && userTypes.contains(UserConstants.USER_TYPE_TEACHER)) {
            List<String> res = userService.listObjs(new QueryWrapper<User>()
                            .select("distinct(username)")
                            .eq("user_type", UserConstants.USER_TYPE_TEACHER)
                            .in(null != collegeCodes && collegeCodes.size() > 0, "college_code", collegeCodes),
                    d -> {
                        return d.toString();
                    });
            list.addAll(res);
        }

        if (null != userTypes && userTypes.size() > 0 && userTypes.contains(UserConstants.USER_TYPE_STUDENT)) {
            List<String> res = studentService.listObjs(new QueryWrapper<Student>()
                            .select("distinct(xh)")
                            .in(null != yearIds && yearIds.size() > 0, "year_id", yearIds)
                            .in(null != xls && xls.size() > 0, "xl", xls)
                            .in(null != collegeCodes && collegeCodes.size() > 0, "college_code", collegeCodes),
                    d -> {
                        return d.toString();
                    });
            list.addAll(res);
        }

        if (StrUtil.isBlank(strings)) {
            return new ArrayList<>();
        }

        List<String> stringList = Arrays.stream(strings.split(",")).distinct().collect(Collectors.toList());
        list.addAll(stringList);
        return list;
    }

    /**
     * 保存推送记录
     *
     * @param xhList
     * @param pushResult
     * @param noticeId
     */
    private List<NoticeRecord> saveRecord(List<String> xhList, JsonResult pushResult, Integer noticeId) {
        int code = pushResult.getCode();
        String msg = pushResult.getMsg();

        String e = "";
        String m = "";
        String ucsId = "";
        List<String> errorList = new ArrayList<>();

        if (Constants.RESULT_OK_CODE == code) {
            JSONObject jsonObject = JSONUtil.parseObj(pushResult.getData());
            e = jsonObject.getStr("e");
            m = jsonObject.getStr("m");
            String d = jsonObject.getStr("d");
            if (SUCCESS.equals(e)) {
                JSONObject result = JSONUtil.parseObj(d);
                ucsId = result.getStr("ucs_id");

                String errorData = result.getStr("error_data");
                if (!"[]".equals(errorData)) {
                    JSONObject errorDataJson = JSONUtil.parseObj(errorData);
                    for (int i = 1; i < xhList.size(); i++) {
                        String error = errorDataJson.getStr(i + "");
                        if (!StrUtil.isBlank(error)) {
                            errorList.add(error);
                        } else {
                            break;
                        }
                    }
                }
            }
        }

        List<NoticeRecord> recordList = new ArrayList<>();
        for (String xh : xhList) {
            NoticeRecord record = new NoticeRecord();
            record.setNoticeId(noticeId);
            record.setXh(xh);
            record.setDescription(m);
            // 整个推送请求错误
            if (Constants.RESULT_ERROR_CODE == code) {
                record.setState(FAIL);
                record.setDescription(msg);
            }

            // 推送请求不成功
            if (!SUCCESS.equals(e)) {
                record.setState(FAIL);
            }

            // 推送请求成功
            if (SUCCESS.equals(e)) {
                record.setState(SUCCESS);
                record.setUcsId(ucsId);
                if (errorList.contains(xh)) {
                    record.setState(FAIL);
                    record.setDescription("不存在或者无对应通讯方式的学工号列表");
                }
            }
            recordList.add(record);
        }
        noticeRecordService.saveBatch(recordList);
        return recordList;
    }

    @Override
    public JsonResult importExcel(MultipartFile file) {
        try {
            int startRow = 1;
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream(), 0);
            List<List<Object>> list = reader.read(startRow);

            // 进行非空和重复检查
            StringBuilder sb = new StringBuilder();
            sb.append(CoreUtil.excelCheckBlank(list, startRow, 0));
            sb.append(CoreUtil.excelCheckRepeat(list, startRow, 0));
            if (!sb.toString().isEmpty()) {
                return JsonResult.error(sb.toString());
            }

            String result = "";
            int size = list.size();
            for (int i = 0; i < list.size(); i++) {
                List<Object> objects = list.get(i);
                String xh = String.valueOf(objects.get(0)).trim();
                result += xh + ",";
            }

            reader.close();
            return JsonResult.ok().setData(result).put("size", size).setMsg("上传成功");
        } catch (IOException e) {
            throw new BusinessException("导入异常：" + e.getMessage());
        }
    }

    @Override
    public void exportExcel(Integer noticeId, HttpServletResponse response) {
        Notice notice = baseMapper.selectById(noticeId);
        List<NoticeRecordExport> list = noticeRecordService.exportList(noticeId);

        ExcelWriter writer = ExcelUtil.getWriter(true);

        writer.passCurrentRow();
        writer.passRows(1);
        writer.merge(0, 0, 0, 3, "备注：" + notice.getRemark(), false);
        writer.merge(1, 1, 0, 3, "内容：" + notice.getContent(), false);

        excelAddHeaderAlias(writer);

        writer.write(list, true);

        // 设置列自适应宽 必须在write以后
        ExcelUtils.autoColumnMaxSizeAll(writer.getSheet());

        String fileName = "record-" + DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN) + ".xlsx";

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        try {
            OutputStream out = response.getOutputStream();
            writer.flush(out, true);
            writer.close();
            IoUtil.close(out);
        } catch (IOException e) {
            writer.close();
        } finally {
            writer.close();
        }
    }

    public void excelAddHeaderAlias(ExcelWriter writer) {
        writer.addHeaderAlias("xh", "学工号");
        writer.addHeaderAlias("state", "发送状态");
        writer.addHeaderAlias("description", "发送状态描述");
        writer.addHeaderAlias("createTime", "发送时间");
    }
}
